{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "eKPK-_pj-BE3"
      },
      "source": [
        "# Simple RAG (Retrieval-Augmented Generation) System\n",
        "\n",
        "## Overview\n",
        "\n",
        "This code implements a basic Retrieval-Augmented Generation (RAG) system for processing and querying PDF documents. The system encodes the document content into a vector store, which can then be queried to retrieve relevant information.\n",
        "\n",
        "## Key Components\n",
        "\n",
        "1. PDF processing and text extraction\n",
        "2. Text chunking for manageable processing\n",
        "3. Vector store creation using [FAISS](https://engineering.fb.com/2017/03/29/data-infrastructure/faiss-a-library-for-efficient-similarity-search/) and OpenAI embeddings\n",
        "4. Retriever setup for querying the processed documents\n",
        "5. Evaluation of the RAG system\n",
        "\n",
        "## Method Details\n",
        "\n",
        "### Document Preprocessing\n",
        "\n",
        "1. The PDF is loaded using PyPDFLoader.\n",
        "2. The text is split into chunks using RecursiveCharacterTextSplitter with specified chunk size and overlap.\n",
        "\n",
        "### Text Cleaning\n",
        "\n",
        "A custom function `replace_t_with_space` is applied to clean the text chunks. This likely addresses specific formatting issues in the PDF.\n",
        "\n",
        "### Vector Store Creation\n",
        "\n",
        "1. OpenAI embeddings are used to create vector representations of the text chunks.\n",
        "2. A FAISS vector store is created from these embeddings for efficient similarity search.\n",
        "\n",
        "### Retriever Setup\n",
        "\n",
        "1. A retriever is configured to fetch the top 2 most relevant chunks for a given query.\n",
        "\n",
        "### Encoding Function\n",
        "\n",
        "The `encode_pdf` function encapsulates the entire process of loading, chunking, cleaning, and encoding the PDF into a vector store.\n",
        "\n",
        "## Key Features\n",
        "\n",
        "1. Modular Design: The encoding process is encapsulated in a single function for easy reuse.\n",
        "2. Configurable Chunking: Allows adjustment of chunk size and overlap.\n",
        "3. Efficient Retrieval: Uses FAISS for fast similarity search.\n",
        "4. Evaluation: Includes a function to evaluate the RAG system's performance.\n",
        "\n",
        "## Usage Example\n",
        "\n",
        "The code includes a test query: \"What is the main cause of climate change?\". This demonstrates how to use the retriever to fetch relevant context from the processed document.\n",
        "\n",
        "## Evaluation\n",
        "\n",
        "The system includes an `evaluate_rag` function to assess the performance of the retriever, though the specific metrics used are not detailed in the provided code.\n",
        "\n",
        "## Benefits of this Approach\n",
        "\n",
        "1. Scalability: Can handle large documents by processing them in chunks.\n",
        "2. Flexibility: Easy to adjust parameters like chunk size and number of retrieved results.\n",
        "3. Efficiency: Utilizes FAISS for fast similarity search in high-dimensional spaces.\n",
        "4. Integration with Advanced NLP: Uses OpenAI embeddings for state-of-the-art text representation.\n",
        "\n",
        "## Conclusion\n",
        "\n",
        "This simple RAG system provides a solid foundation for building more complex information retrieval and question-answering systems. By encoding document content into a searchable vector store, it enables efficient retrieval of relevant information in response to queries. This approach is particularly useful for applications requiring quick access to specific information within large documents or document collections."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XvUHveE5-BE3"
      },
      "source": [
        "# Package Installation and Imports\n",
        "\n",
        "The cell below installs all necessary packages required to run this notebook.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "xVaHWFwh-BE4",
        "outputId": "d725de55-3b6e-45ad-a599-ca0f4e7d1698"
      },
      "outputs": [],
      "source": [
        "# Install required packages\n",
        "!pip install pypdf==5.6.0\n",
        "!pip install PyMuPDF==1.26.1\n",
        "!pip install python-dotenv==1.1.0\n",
        "!pip install langchain-community==0.3.25\n",
        "!pip install langchain_openai==0.3.23\n",
        "!pip install rank_bm25==0.2.2\n",
        "!pip install faiss-cpu==1.11.0\n",
        "!pip install deepeval==3.1.0"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "GGVwzS4v-BE5",
        "outputId": "e5a4d759-3a78-4b76-9670-0375c78757be"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Cloning into 'RAG_TECHNIQUES'...\n",
            "remote: Enumerating objects: 1531, done.\u001b[K\n",
            "remote: Counting objects: 100% (808/808), done.\u001b[K\n",
            "remote: Compressing objects: 100% (359/359), done.\u001b[K\n",
            "remote: Total 1531 (delta 549), reused 458 (delta 449), pack-reused 723 (from 2)\u001b[K\n",
            "Receiving objects: 100% (1531/1531), 34.20 MiB | 25.58 MiB/s, done.\n",
            "Resolving deltas: 100% (962/962), done.\n"
          ]
        }
      ],
      "source": [
        "# Clone the repository to access helper functions and evaluation modules\n",
        "!git clone https://github.com/NirDiamant/RAG_TECHNIQUES.git\n",
        "import sys\n",
        "sys.path.append('RAG_TECHNIQUES')\n",
        "\n",
        "# If you need to run with the latest data\n",
        "# !cp -r RAG_TECHNIQUES/data ."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ha6ASRqq-BE5",
        "outputId": "04acf50b-926e-4ff8-a5a3-6ecf11f74dd2"
      },
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "<ipython-input-2-622825101>:26: LangChainDeprecationWarning: As of langchain-core 0.3.0, LangChain uses pydantic v2 internally. The langchain_core.pydantic_v1 module was a compatibility shim for pydantic v1, and should no longer be used. Please update the code to import from Pydantic directly.\n",
            "\n",
            "For example, replace imports like: `from langchain_core.pydantic_v1 import BaseModel`\n",
            "with: `from pydantic import BaseModel`\n",
            "or the v1 compatibility namespace if you are working in a code base that has not been fully upgraded to pydantic 2 yet. \tfrom pydantic.v1 import BaseModel\n",
            "\n",
            "  from helper_functions import (EmbeddingProvider,\n"
          ]
        }
      ],
      "source": [
        "import os\n",
        "import sys\n",
        "from dotenv import load_dotenv\n",
        "from google.colab import userdata\n",
        "\n",
        "\n",
        "\n",
        "# Load environment variables from a .env file\n",
        "load_dotenv()\n",
        "\n",
        "# Set the OpenAI API key environment variable (comment out if not using OpenAI)\n",
        "if not userdata.get('OPENAI_API_KEY'):\n",
        "    os.environ[\"OPENAI_API_KEY\"] = input(\"Please enter your OpenAI API key: \")\n",
        "else:\n",
        "    os.environ[\"OPENAI_API_KEY\"] = userdata.get('OPENAI_API_KEY')\n",
        "\n",
        "# Original path append replaced for Colab compatibility\n",
        "\n",
        "from langchain.document_loaders import PyPDFLoader\n",
        "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
        "from helper_functions import (EmbeddingProvider,\n",
        "                              retrieve_context_per_question,\n",
        "                              replace_t_with_space,\n",
        "                              get_langchain_embedding_provider,\n",
        "                              show_context)\n",
        "\n",
        "from evaluation.evalute_rag import evaluate_rag\n",
        "\n",
        "from langchain.vectorstores import FAISS\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HVWdzMuw-BE5"
      },
      "source": [
        "### Read Docs"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "AagmKvC0-BE6",
        "outputId": "889d0df0-57b3-4e31-a3d5-7ab52ed33892"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "--2025-06-14 07:31:48--  https://raw.githubusercontent.com/NirDiamant/RAG_TECHNIQUES/main/data/Understanding_Climate_Change.pdf\n",
            "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n",
            "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n",
            "HTTP request sent, awaiting response... 200 OK\n",
            "Length: 206372 (202K) [application/octet-stream]\n",
            "Saving to: ‘data/Understanding_Climate_Change.pdf’\n",
            "\n",
            "\r          data/Unde   0%[                    ]       0  --.-KB/s               \rdata/Understanding_ 100%[===================>] 201.54K  --.-KB/s    in 0.03s   \n",
            "\n",
            "2025-06-14 07:31:48 (5.89 MB/s) - ‘data/Understanding_Climate_Change.pdf’ saved [206372/206372]\n",
            "\n",
            "--2025-06-14 07:31:48--  https://raw.githubusercontent.com/NirDiamant/RAG_TECHNIQUES/main/data/Understanding_Climate_Change.pdf\n",
            "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.111.133, 185.199.108.133, ...\n",
            "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.\n",
            "HTTP request sent, awaiting response... 200 OK\n",
            "Length: 206372 (202K) [application/octet-stream]\n",
            "Saving to: ‘data/Understanding_Climate_Change.pdf’\n",
            "\n",
            "data/Understanding_ 100%[===================>] 201.54K  --.-KB/s    in 0.03s   \n",
            "\n",
            "2025-06-14 07:31:48 (5.77 MB/s) - ‘data/Understanding_Climate_Change.pdf’ saved [206372/206372]\n",
            "\n"
          ]
        }
      ],
      "source": [
        "# Download required data files\n",
        "import os\n",
        "os.makedirs('data', exist_ok=True)\n",
        "\n",
        "# Download the PDF document used in this notebook\n",
        "!wget -O data/Understanding_Climate_Change.pdf https://raw.githubusercontent.com/NirDiamant/RAG_TECHNIQUES/main/data/Understanding_Climate_Change.pdf\n",
        "!wget -O data/Understanding_Climate_Change.pdf https://raw.githubusercontent.com/NirDiamant/RAG_TECHNIQUES/main/data/Understanding_Climate_Change.pdf\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "id": "KF5O4Wk4-BE6"
      },
      "outputs": [],
      "source": [
        "path = \"data/Understanding_Climate_Change.pdf\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "P0MAcEpU-BE6"
      },
      "source": [
        "### Encode document"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "id": "38suL-tJ-BE7"
      },
      "outputs": [],
      "source": [
        "def encode_pdf(path, chunk_size=1000, chunk_overlap=200):\n",
        "    \"\"\"\n",
        "    Encodes a PDF book into a vector store using OpenAI embeddings.\n",
        "\n",
        "    Args:\n",
        "        path: The path to the PDF file.\n",
        "        chunk_size: The desired size of each text chunk.\n",
        "        chunk_overlap: The amount of overlap between consecutive chunks.\n",
        "\n",
        "    Returns:\n",
        "        A FAISS vector store containing the encoded book content.\n",
        "    \"\"\"\n",
        "\n",
        "    # Load PDF documents\n",
        "    loader = PyPDFLoader(path)\n",
        "    documents = loader.load()\n",
        "\n",
        "    # Split documents into chunks\n",
        "    text_splitter = RecursiveCharacterTextSplitter(\n",
        "        chunk_size=chunk_size, chunk_overlap=chunk_overlap, length_function=len\n",
        "    )\n",
        "    texts = text_splitter.split_documents(documents)\n",
        "    cleaned_texts = replace_t_with_space(texts)\n",
        "\n",
        "    # Create embeddings (Tested with OpenAI and Amazon Bedrock)\n",
        "    embeddings = get_langchain_embedding_provider(EmbeddingProvider.OPENAI)\n",
        "    #embeddings = get_langchain_embedding_provider(EmbeddingProvider.AMAZON_BEDROCK)\n",
        "\n",
        "    # Create vector store\n",
        "    vectorstore = FAISS.from_documents(cleaned_texts, embeddings)\n",
        "\n",
        "    return vectorstore"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "id": "8aysEXUh-BE7"
      },
      "outputs": [],
      "source": [
        "chunks_vector_store = encode_pdf(path, chunk_size=1000, chunk_overlap=200)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YaCrjGRA-BE7"
      },
      "source": [
        "### Create retriever"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "id": "wgVN3-y1-BE7"
      },
      "outputs": [],
      "source": [
        "chunks_query_retriever = chunks_vector_store.as_retriever(search_kwargs={\"k\": 2})"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jQJUmIXS-BE7"
      },
      "source": [
        "### Test retriever"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "sC9jmnHM-BE7",
        "outputId": "463219f6-d06c-4031-d348-f89a66ea15c0"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Context 1:\n",
            "Chapter 2: Causes of Climate Change \n",
            "Greenhouse Gases \n",
            "The primary cause of recent climate change is the increase in greenhouse gases in the \n",
            "atmosphere. Greenhouse gases, such as carbon dioxide (CO2), methane (CH4), and nitrous \n",
            "oxide (N2O), trap heat from the sun, creating a \"greenhouse effect.\" This effect is essential \n",
            "for life on Earth, as it keeps the planet warm enough to support life. However, human \n",
            "activities have intensified this natural process, leading to a warmer climate. \n",
            "Fossil Fuels \n",
            "Burning fossil fuels for energy releases large amounts of CO2. This includes coal, oil, and \n",
            "natural gas used for electricity, heating, and transportation. The industrial revolution marked \n",
            "the beginning of a significant increase in fossil fuel consumption, which continues to rise \n",
            "today. \n",
            "Coal\n",
            "\n",
            "\n",
            "Context 2:\n",
            "Most of these climate changes are attributed to very small variations in Earth's orbit that \n",
            "change the amount of solar energy our planet receives. During the Holocene epoch, which \n",
            "began at the end of the last ice age, human societies flourished, but the industrial era has seen \n",
            "unprecedented changes. \n",
            "Modern Observations \n",
            "Modern scientific observations indicate a rapid increase in global temperatures, sea levels, \n",
            "and extreme weather events. The Intergovernmental Panel on Climate Change (IPCC) has \n",
            "documented these changes extensively. Ice core samples, tree rings, and ocean sediments \n",
            "provide a historical record that scientists use to understand past climate conditions and \n",
            "predict future trends. The evidence overwhelmingly shows that recent changes are primarily \n",
            "driven by human activities, particularly the emission of greenhouse gases. \n",
            "Chapter 2: Causes of Climate Change \n",
            "Greenhouse Gases \n",
            "The primary cause of recent climate change is the increase in greenhouse gases in the\n",
            "\n",
            "\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "/content/RAG_TECHNIQUES/helper_functions.py:143: LangChainDeprecationWarning: The method `BaseRetriever.get_relevant_documents` was deprecated in langchain-core 0.1.46 and will be removed in 1.0. Use :meth:`~invoke` instead.\n",
            "  docs = chunks_query_retriever.get_relevant_documents(question)\n"
          ]
        }
      ],
      "source": [
        "test_query = \"What is the main cause of climate change?\"\n",
        "context = retrieve_context_per_question(test_query, chunks_query_retriever)\n",
        "show_context(context)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MbrMQXnX-BE8"
      },
      "source": [
        "### Evaluate results"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "-Cjmro4s-BE8",
        "outputId": "45d0a93a-6313-4cdf-a30f-cf7ca9525f0e"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'questions': ['1. **Multiple Choice: Causes of Climate Change**',\n",
              "  '   - What is the primary cause of the current climate change trend?',\n",
              "  '     A) Solar radiation variations',\n",
              "  '     B) Natural cycles of the Earth',\n",
              "  '     C) Human activities, such as burning fossil fuels',\n",
              "  '     D) Volcanic eruptions',\n",
              "  '',\n",
              "  '2. **True or False: Climate Change Impacts**',\n",
              "  '   - True or False: Climate change only affects the temperature of the planet, not weather patterns, sea levels, or ecosystems.',\n",
              "  '',\n",
              "  '3. **Short Answer: Mitigation Strategies**',\n",
              "  '   - Describe two effective strategies that could be implemented to mitigate the effects of climate change.',\n",
              "  '',\n",
              "  '4. **Matching: Climate Change Terminology**',\n",
              "  '   - Match the following terms with their correct definitions:',\n",
              "  '     A) Greenhouse Gases',\n",
              "  '     B) Carbon Footprint',\n",
              "  '     C) Renewable Energy',\n",
              "  '     D) Adaptation',\n",
              "  '     - Definitions:',\n",
              "  '       1. The total amount of greenhouse gases produced to directly and indirectly support human activities, usually expressed in equivalent tons of carbon dioxide (CO2).',\n",
              "  \"       2. Gases in Earth's atmosphere that trap heat, such as CO2, methane, and nitrous oxide.\",\n",
              "  '       3. Adjusting practices, processes, and capital in response to the risks posed by climate change.',\n",
              "  '       4. Energy from sources that are not depleted when used, such as wind or solar power.',\n",
              "  '',\n",
              "  '5. **Essay: International Cooperation**',\n",
              "  '   - Discuss the importance of international cooperation in combating climate change. Include examples of international agreements or policies that have been implemented to address the issue.'],\n",
              " 'results': ['```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 4,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 5,\\n  \"Conciseness\": 4\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 2,\\n  \"Completeness\": 1,\\n  \"Conciseness\": 2\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 3,\\n  \"Completeness\": 2,\\n  \"Conciseness\": 2\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 4,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 1,\\n  \"Completeness\": 1,\\n  \"Conciseness\": 2\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 1,\\n  \"Completeness\": 1,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 4,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n    \"Relevance\": 5,\\n    \"Completeness\": 5,\\n    \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 1,\\n  \"Completeness\": 1,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n    \"Relevance\": 4,\\n    \"Completeness\": 2,\\n    \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 4,\\n  \"Completeness\": 3,\\n  \"Conciseness\": 2\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 1,\\n  \"Completeness\": 1,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 4,\\n  \"Completeness\": 2,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 3,\\n  \"Completeness\": 2,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 4,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 4,\\n  \"Completeness\": 3,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 4,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 4,\\n  \"Completeness\": 3,\\n  \"Conciseness\": 2\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 1,\\n  \"Completeness\": 1,\\n  \"Conciseness\": 2\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 4,\\n  \"Completeness\": 3,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 5,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 4,\\n  \"Completeness\": 3,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 4,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 1,\\n  \"Completeness\": 1,\\n  \"Conciseness\": 3\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 5,\\n  \"Completeness\": 4,\\n  \"Conciseness\": 4\\n}\\n```',\n",
              "  '```json\\n{\\n  \"Relevance\": 4,\\n  \"Completeness\": 3,\\n  \"Conciseness\": 4\\n}\\n```'],\n",
              " 'average_scores': None}"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "#Note - this currently works with OPENAI only\n",
        "evaluate_rag(chunks_query_retriever)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "77cFwBpjVMWP"
      },
      "outputs": [],
      "source": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "![](https://europe-west1-rag-techniques-views-tracker.cloudfunctions.net/rag-techniques-tracker?notebook=all-rag-techniques--simple-rag)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": []
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": ".venv",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
